ES6 functions改动
ES6函数的改变不算太大,都是一些其他语言早就有的功能,而Javascript一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函数(其他语言称之为LAMBDA表达式),一种对匿名函数的一种简写方式,以下来探讨一下函数在ES6中的一些改变:
1. 默认参数(default parameters)
2. 任意参数(rest parameters ...)
3. spread 操作符(...)
4. new.target 元属性
5. 箭头函数( => )
6. 其他一些改动(miscellaneous)
1.默认参数
ES6之前一直是通过其他方法来模拟默认参数的,例如逻辑或||符号,ES6版本真正意义上支持这种便利的写法。
// ES5模拟默认参数
function person(name, age) {
name = name || "James";
age = age || "18";
console.log(name + " " + age);
}
// 一般情况下这种写法是没问题的,当逻辑或前面的值为falsy值,整个表达式返回后面的值
// 例如:
person("Louis"); // ok
person(); // ok
person(undefined, 20); // ok
person("baby", 0); // "baby 18" error, 0为falsy值
上面可以看出这种写法是有一定问题的,各种JS库给出了另一种写法
function person(name, age) {
if (typeof name === "undefined") {
name = name || "James";
}
if (typeof age === "undefined") {
age = age || "18";
}
console.log(name + " " + age);
}
person(undefined, 0); // ok "James 0"
ES6写法
function person(name = "James", age = 18) {
console.log(name + " " + age);
}
// 1各种写法 默认参数出现在中间
function getRequest(url, timeout = 2000, callback) {
// do something
}
gerRequest("/foo", undefined, function() {
});
// 2默认参数表达式
let value = 5;
function getValue() {
return value++;
}
function add(first, second = getValue()) {
return first + second;
}
add(3); // 8
add(3); // 9
add(1, 1); // 2
// 3后面参数引用前面参数
function add(first, second = first) {
return first + second;
}
add(2); // 4
add(3, 4); // 7
默认参数TDZ(暂时死区)情况:
// 上面的第三种写法,若写成第一个参数引用第二个参数
function add(first = second, second) {
return first + second;
}
add(1, 1); // ok 2
add(undefined, 4); // THROW AN ERROR 第二个参数未声明就引用就会抛出错误
// 就相当于
let first = second; // error
let second = 4;
2.任意参数
ES6任意参数用 ...
表示,任意参数和arguments之间的差别
ES5使用arguments参数来实现对象属性拷贝:
function pick(object) {
var result = Object.create(null); // 创建一个对象
// 从第二个参数开始
for (var i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] = object[arguments[i]];
}
return result;
}
// arguments将object也计入,所以除开第一个参数要减1
var book = {
title: "understanding ES6",
author: "Nicholas C.Zakes",
year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016
上面的pick函数看上去不够直观,因为除第一个参数外不知道要添加几个参数,使用新语法
function pick(object, ...keys) {
var result = Object.create(null); // 创建一个对象
for (var i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
// keys将object不计入在其内
var book = {
title: "understanding ES6",
author: "Nicholas C.Zakes",
year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016
使用rest parameters注意事项:
1.要将任意参数放到函数的最后,不能放在中间位置
2.不能用于对象字面量setter中
function pick(object, ...keys, last) { //... }
// 语法错误
let object = {
set name(...value) {
// do something
}
};
// 语法错误
3.spread操作符
spread操作符和rest parameters一样,都使用 ...
表示,spread操作符允许我们将数组中的参数一个一个传入函数中
例如:
// Math.max()函数, 一般可以加入任意个参数
Math.max(12, 13, 14, 15); // 15
// 以数组的形式
var arr = [1, 2, 3, 4];
Math.max.apply(null, arr); // 4
// 使用 "..."
Math.max(...arr); // 4
// 还可以加入其它的一些参数
Math.max(...arr, 5, 10); // 10
将一个数组去重:
var arr = [1, 2, 2, 4, 4];
// 使用Set将重复的去掉,然后将set对象转变为数组
var mySet = new Set(arr); // mySet {1, 2, 4}
// 方法1,使用Array.from转变为数组
// var arr = Array.from(mySet); // [1, 2, 4]
// 方法2,使用spread操作符
var arr = [...arr]; // [1, 2, 4]
// 方法3, 传统forEach
var arr2 = [];
mySet.forEach(v => arr2.push(v));
4.new.target元属性
函数内部有两个方法 [[call]] 和 [[construct]] (箭头函数没有这个方法),当使用new 操作符时, 函数内部调用 [[construct]], 创建一个新实例,this指向这个实例; 不使用new 操作符时, 函数内部调用 [[call]]。
判断一个函数是否使用new操作符,ES5的方法:
function Person(name) {
if (this instanceof Person) {
this.name = name;
} else {
throw new Error("You must use new operator");
}
}
var p = new Person("James"); // ok
var p = Person("James"); // error
// 但是可以通过其他方式绕过这种错误
var notPerson = Person.call(p, "Nicholas"); // works
ES6 通过new.target 来判断是否使用new,元属性 是指一个提供目标相关额外信息(比如new)的非对象属性。
function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name;
} else {
throw new Errow("You must use new operator");
}
}
var p = new Person("James"); // ok
var notPerson = Person.call(p, "Louis"); // error
5.箭头函数
箭头函数有以下几个方面的特点:
- this, super, arguments和arguments的值由最近一个包含它的非箭头函数定义。(No this, superm arguments and new.target bindings);
- 箭头函数内部没有 [[construct]]方法, 因此不能当作构造器,使用new操作符;
- 不存在原型(No prototype);
- 不能改变this, 在整个箭头函数生命周期this值保持不变;
- 不存在arguments对象,不过包含它的函数存在,箭头函数依靠命名参数和rest parameters;
- 不能拥有重复的命名参数,ES5只有严格模式下才不允许
1.箭头函数语法
// 语法很简单
let sum = (n1, n2) => n1 + n2;
// 相当于
let sum = function(n1, n2) {
return n1 + n2;
};
let getTempItem = id => ({ id: id, name: "Temp" });
// 相当于
let getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
2.没有this绑定
let PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", function(event) {
this.doSomething(event.type); // error
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
// init函数中的this.doSomething,this指向的是函数内部document对象,
// 而不是PageHandler对象
使用箭头函数改写:
let PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", event => this.doSomething(evnet.type)
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
// 此处箭头函数this指向包含它的函数,即init,init为PageHandler的方法,
// this指向PageHandler对象实例
3.不能使用new
var MyType = () => {};
var obj = new MyType(); // Error
4.没有arguments对象
箭头函数没有arguments对象,但是可以使用包含函数中的arguments对象
function createArrowFunctionReturningFirstArg() {
// arguments 为 createArrowFunctionReturningFirstArg中的对象
return () => arguments[0];
}
var arrowFunction = createArrowFunctionReturningFirstArg(10);
arrFunction(); // 10
6.其他(misllanceous)
其他的一些变化:
- 添加name属性用来判断函数名,用于调试;
- 规范块级别函数(block-level functions),当执行流结束块级别函数退出,块级别函数提升变量到块顶部;
- 对尾部调用(Tail call, 值一个函数返回另一个函数对象)性能进行优化,尤其是都递归函数性能提升很大
总结
- ES6对默认参数的支持;
- 任意参数和spread操作符
- 箭头函数
总体来说,这些改动都是为编写程序提供了极大的便利,不用再使用workaround来解决语法存在的问题,整体来讲,更加符合语言的书写习惯。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。